{UTF-8}
Unit DispProc;
Interface
Uses Dos,Routine,Process,Queue,List;
Type
	PDispatcherProcess = ^TDispatcherProcess;
	TDispatcherProcess = Object
		Private
			{список готовых процессов}
			listReadyProcess:		TList;
			listDelayProcess:		TList;
			listDelayTimeProcess:	TList;
			listKillProcess:		TList;
			Main:					descptr;
			numberProcess:			id_type;
			locked:					boolean;
			time:					longint;
		Public
			{nProcess:  количество процессов; array_proc: массив процедур-тел процессов}
			{pmain - дескриптор процедуры, в которую нужно сделать возвращение}
			Constructor init(nProcess:word; var array_proc; const pmain:descptr);
			Destructor done;
			Procedure activate_next_process;
			Procedure add_new_process(arg:body_process);
			Procedure get_current_process(var arg);
			Procedure start;
			Procedure lock;
			Procedure unlock;
			Function  is_locked:boolean;
			Procedure stop;
			(*метод пытается найти процесс в queueReadyProcess, и если находит,*)
			(*то добавляет его в очередь задержанных процессов*)
			(*процесс, который задерживается, ни в коем случае
			не должен быть текущим*)
			Procedure	delayProcess(var p:TProcess);
			(*метод пытается найти процесс в queueReadyProcess,
			и если находит,*)
			(*то добавляет его в очередь задержанных процессов*)
			Procedure	delayProcessByComparator(var p:TProcess; comp:comparator);
			(*метод пытается найти в queueDelayProcess, и если
			находит*)
			(*то добавляет его обратно в queueReadyProcess*)
			Procedure	resumeProcess(var p:TProcess);
			(*метод пытается найти в listDelayProcess, и если
			находит*)
			(*то добавляет его обратно в listReadyProcess,
			причём так,*)
			(*чтобы возобновлённый процесс начал выполняться
			следующим после текушего*)
			Procedure	resumeProcessNow(var p:TProcess);
			(*метод пытается найти в queueDelayProcess, и если
			находит*)
			(*то добавляет его обратно в queueReadyProcess*)
			Procedure	resumeProcessByComparator(var p:TProcess;
													comp:comparator);
			(*метод пытается найти текущий процесс в
			queueReadyProcess, и если находит*)
			(*то добавляет его в queueDelayTimeProcess*)
			(*управляющий процесс задерживать на время нельзя*)
			Procedure	delayTimeProcess(const T:longint);
			(*Метод пытается найти процесс в queueReadyProcess,
			и если находит*)
			(*то добавляет его в queueKillProcess*)		
			Procedure	killProcess(var p:TProcess);
			(*Метод пытается найти процесс в queueReadyProcess,
			и если находит*)
			(*то добавляет его в queueKillProcess*)		
			Procedure	killProcessByComparator(var p:TProcess; comp:comparator);			
			(*метод активизирует все процессы, которые должны
			быть*)
			(*активизированы в текущий момент времени*)
			Procedure	activisation;
			(*метод уничтожает все процессы, находящиеся в
			queueKillProcess*)
			Procedure	clearKillProcess;
			{метод уничтожает текущий процесс}
			Procedure 	killCurrentProcess;
			{метод уничтожает текущий процесс с помощью
			компаратора (функции сравнитедя двух процессов)}
			Procedure 	killCurrentProcessByComparator(comp:comparator);
			{метод увеличивает значение time на 1}
			Procedure	step_time;
			{метод сбрасывает значение time в 0}
			Procedure	reset_time;
			Function	getNumberReadyProcess:word;
			Function	getNumberDelayProcess:word;
			Procedure	getArrayOfReadyPID(var arg);
			Procedure	getArrayOfDelayPID(var arg);			
			
	end;	
	{возвращает текущий диспетчер процессов}
	Function get_current_dispatcher:PDispatcherProcess;
	Procedure set_current_dispatcher(const ptr:PDispatcherProcess);


	
Implementation

Type
	buffer = array[1..MAX_SIZE] of byte;
	pbuffer = ^buffer;

const
	InterruptOld:word = $8;
	InterruptNew:word = $60;
var
	{текущий диспетчер процессов, с которым работает Handler}
	currentDispatcher: PDispatcherProcess;
	TimerPtr:pointer;	

	
{функция сравнивает два процесса по полю takt}
Function compare_process_by_takt(const p1:pointer; const p2:pointer):boolean;
far;
var
	ptr1,
	ptr2:	PProcess;
begin
	ptr1:=p1;
	ptr2:=p2;
	compare_process_by_takt:=(ptr1^.get_takt >= ptr2^.get_takt);
end;
	
Function get_current_dispatcher;
begin
	get_current_dispatcher:=currentDispatcher;
end;	
Procedure set_current_dispatcher;
begin
	currentDispatcher:=ptr;
end;	
Procedure Handler;
interrupt;
begin
	disable_interrupt;
	asm
		int $60;
	end;
	currentDispatcher^.step_time;
	{writeln(currentDispatcher.is_locked);}
	if (not currentDispatcher^.is_locked) then
		begin
			currentDispatcher^.clearKillProcess;			
			currentDispatcher^.activisation;
			currentDispatcher^.activate_next_process;
		end
	else
		enable_interrupt;
end;
(*
 * * * * * * * * * * * *
 *	TDispatcherProcess *
 * * * * * * * * * * * *
*) 
{$F-}{отключим контроль за выходом иднексов за границы массивов,
 чтобы передать в конструктор массив переменной длины}
Constructor TDispatcherProcess.init;
var
	A:array[1..2] of body_process absolute array_proc;
	i:word;
	currentProcess:	TProcess;
begin
	{диспетчер не заблокирован}
	locked:=false;
	{количество вызовов таймера = 0}
	time:=0;
	{количество процессов  = параметру nProcess}
	numberProcess:=nProcess;
	{инициализируем очередь для готовых процессов}
	listReadyProcess.init(sizeof(TProcess));
	{инициализируем список для задержанных процессов}
	listDelayProcess.init(sizeof(TProcess));
	{инициализируем список процессов, задержанных на время}
	listDelayTimeProcess.init(sizeof(TProcess));
	{инициализируем список процессов, подлежащих уничтожению}
	listKillProcess.init(sizeof(TProcess));
	{если в качестве указателя на процедуру, в которую}
	{нужно вернуться был передан nil, выходим из конструктора}
	if (pmain = nil)then Fail;
	Main := pmain;
	{заполняем очередь готовых процессов с помощью параметров,
	переданных в конструктор}
	for i := 1 to nProcess do
		begin
			currentProcess.init(A[i], i);
			listReadyProcess.push_back(currentProcess);
		end;
end;
{$F+}
Destructor TDispatcherProcess.done;
var
	p:TProcess;
begin
	{уничтожаем очередь готовых процессов}
	while (not listReadyProcess.empty) do
		begin
			if (listReadyProcess.front(p)) then
				p.done;
			listReadyProcess.pop_front;
		end;
	listReadyProcess.done;
	{унитожаем список задержанных процессов}
	while (not listDelayProcess.empty) do
		begin
			if (listDelayProcess.front(p)) then
				p.done;
			listDelayProcess.pop_front;
		end;
	listDelayProcess.done;
	{уничтожаем список процессов, задержанных на время}
	while (not listDelayTimeProcess.empty) do
		begin
			if (listDelayTimeProcess.front(p)) then
				p.done;
			listDelayTimeProcess.pop_front;
		end;
	listDelayTimeProcess.done;
	{унчтожаем список процессов, подлежащих уничтожению}
	while (not listKillProcess.empty) do
		begin
			if (listKillProcess.front(p)) then
				p.done;
			listKillProcess.pop_front;
		end;
	listKillProcess.done;
end;
Procedure TDispatcherProcess.add_new_process;
var
	p:TProcess;
begin
	{увеличиваем количесто процессов}
	inc(numberProcess);
	{создаём новый процесс}
	p.init(arg, numberProcess);
	{блокируем работу диспетчера, чтобы не происходило передачи
	управления от одного процесса другому}
	lock;
	{в список готовых процессов добавляем новй процесс}
	listReadyProcess.push_back(p);
	{разблокировываем работу диспетчера}
	unlock;
end;
Procedure TDispatcherProcess.get_current_process;
var
	nByte:	t_size;
	buf:	pbuffer;	
	a:		array[1..MAX_SIZE] of byte absolute arg;
	i:t_size;
	cur:	TProcess;
begin
	nByte:=	sizeof(TProcess);
	buf:=@cur;
	{если в очереди есть хоть один процесс}
	if (listReadyProcess.front(cur)) then
		begin
			buf:=@cur;
			for i:=1 to nByte do
				A[i]:=buf^[i];
		end;
end;
Procedure TDispatcherProcess.activate_next_process;
var
	nextProcess:TProcess;
	curProcess:	TProcess;
begin
	{если в очереди есть процессы, то происходит передача
	управления от теущего к следующему}
	{иначе ничего не происходит}
	if(listReadyProcess.front(curProcess))then
		begin
			listReadyProcess.move_front_to_back;
			if (listReadyProcess.front(nextProcess)) then
				begin
				writeln('transfer: pid:', curProcess.get_id, ' -> pid:', nextProcess.get_id);
				curProcess.TransferByProcess(nextProcess)
				end
			else
				begin
					enable_interrupt;
					exit;
				end;

		end
	else
		begin
			enable_interrupt;
			exit;
		end;

end;
Procedure TDispatcherProcess.start;
var
	curProcess:	TProcess;
begin
	{если в очереди готовых сообщений есть процессы,}
	{передаём управление текущему процессу}
	if (listReadyProcess.front(curProcess)) then
		begin
			disable_interrupt;
			GetIntVec(InterruptOld, TimerPtr);
			SetIntVec(InterruptNew, TimerPtr);
			SetIntVec(InterruptOld, @Handler);
			Transfer(Main, curProcess.get_descriptor);
		end;
end;
Procedure TDispatcherProcess.stop;
var
	curProcess:	TProcess;
begin
	{если в очереди готовых процессов есть процессы, }
	{передаём управление от текущего процесса в главный процесс
	(программу)}
	if (listReadyProcess.front(curProcess)) then
		begin
			disable_interrupt;
			SetIntVec(InterruptOld, TimerPtr);
			CurProcess.TransferByDescriptor(Main);
		end;
end;
Procedure TDispatcherProcess.lock;
begin
	locked:=true;
end;
Procedure TDispatcherProcess.unlock;
begin
	locked:=false;
end;
Function TDispatcherProcess.is_locked;
begin
	is_locked:=locked;
end;
Procedure TDispatcherProcess.delayProcess;
var
	it:ListIterator;
	temp:TProcess;
	cur:TProcess;
begin
	{ищем в списке готовых процессов процесс p}
	it:= listReadyProcess.find(p);
	{если нашли}
	if (it <> nil) then
		begin
			{блокируем диспетчер}
			lock;
			listReadyProcess.front(cur);
			{если мы пытаемся задержать текущий исполняемый процесс}
			if (cur.equal(p)) then
				begin
					{нужно перед задержкой текущего процесса
					передать управление следующему}
					{переносим первый элемент в конец списка}	
					listReadyProcess.move_front_to_back;
					if (listReadyProcess.front(temp)) then
						begin
							{удаляем из конца списка тот самый теущий процесс}
							listReadyProcess.pop_back;
							{помещаем в список процессов,
							подлежащих уничтожению, теущий
							процесс}
							listDelayProcess.push_back(cur);
							{разблокировываем диспетчер}
							unlock;
							{передаём управление следующему
							процессу, то есть выходим из этого
							метода}
							writeln('transfer: pid:', cur.get_id, ' -> pid:',temp.get_id);
							cur.TransferByProcess(temp);
						end;					
				end
			else
				begin
					{записываем найденный процесс в временную переменную}		
					listReadyProcess.get(it, temp);
					{удаляем из списка готовых процессов}
					listReadyProcess.remove(it);
					{записываем заданный процесс в список задержанных процессов}
					listDelayProcess.push_back(temp);
					{разблокировываем диспетчер}
					unlock;
				end;
		end;
end;
Procedure TDispatcherProcess.delayProcessByComparator;
var
	it:ListIterator;
	temp,
	cur:TProcess;
begin
	{ищем заданный процесс с помощью компаратора comp в списке}
	it:=listReadyProcess.find_by_comparator(@p, comp);
	{если нашли}
	if (it <> nil) then
		begin
			{блокируем диспетчер}
			lock;
			listReadyProcess.front(cur);
			{если мы пытаемся задержать текущий исполняемый процесс}
			if (cur.equal(p)) then
				begin
					{нужно перед задержкой текущего процесса
					передать управление следующему}
					{переносим перывй элемент в конец списка}	
					listReadyProcess.move_front_to_back;
					if (listReadyProcess.front(temp)) then
						begin
							{удаляем из конца списка тот самый теущий процесс}
							listReadyProcess.pop_back;
							{помещаем в список процессов,
							подлежащих уничтожению, текущий
							процесс}
							listDelayProcess.push_back(cur);
							{разблокировываем диспетчер}
							unlock;
							{передаём управление следующему
							процессу, то есть выходим из этого
							метода}
							writeln('transfer: pid:', cur.get_id, ' -> pid:',temp.get_id);
							cur.TransferByProcess(temp);
						end;					
				end
			else
				begin
					{записываем найденный процесс в временную
					переменную}		
					listReadyProcess.get(it, temp);
					{удаляем из списка готовых процессов}
					listReadyProcess.remove(it);
					{записываем заданный процесс в список
					задержанных процессов}
					listDelayProcess.push_back(temp);
					{разблокировываем диспетчер}
					unlock;
				end;
		end;
end;
Procedure TDispatcherProcess.resumeProcess;
var
	it:ListIterator;
	temp:TProcess;
begin
	{ищем заданный процесс в списке задержанных процессов}
	it:=listDelayProcess.find(p);
	{если нашли}
	if (it <> nil) then
		begin
			{блокируем диспетчер}
			lock;
			{записываем найденный процесс в временную
			переменную}
			listDelayProcess.get(it, temp);
			{удаляем из списка задержанных процессов}
			listDelayProcess.remove(it);
			{добавляем в список готовых процессов}
			listReadyProcess.push_back(temp);
			{разблокировываем диспетчер}
			unlock;
		end;
end;
Procedure TDispatcherProcess.resumeProcessNow;
var
	it:		ListIterator;
	temp:	TProcess;
	cur:	TProcess;
begin
	{пытаемся найти заданный процесс в списке задержанных}
	it:=listDelayProcess.find(p);
	{если нашли}
	if (it <> nil) then
		begin
			{блокируем диспетчер}
			lock;
			{записываем найденный процесс в
			временную переменную}
			listDelayProcess.get(it, temp);
			{удаляем его из списка задержанных процессов}
			listDelayProcess.remove(it);
			{если в списке готовых процессов только 1 или вообще
			нет, то
			добавляем в конец списка новый процесс}
			if (listReadyProcess.get_next(listDelayProcess.get_first) = nil) then
				begin
					listReadyProcess.push_back(temp);
				end
			else
				begin
					listReadyProcess.insert(listReadyProcess.get_next(listReadyProcess.get_first),
											temp)
				end;
			{разблокировываем диспетчер}
			unlock;
		end;
end;
Procedure TDispatcherProcess.resumeProcessByComparator;
var
	it:ListIterator;
	temp:TProcess;
begin
	{ищем заданный процесс в списке задержанных процессов}
	{с помощью компаратора}
	it:=listDelayProcess.find_by_comparator(@p, comp);
	{если нашли}
	if (it <> nil) then
		begin
			{блокируем диспетчер}
			lock;
			{записываем найденный процесс в временную переменную}
			listDelayProcess.get(it, temp);
			{удаляем найденный процесс}
			listDelayProcess.remove(it);
			{добавляем найденный процесс в список готовых процессов}
			listReadyProcess.push_back(temp);
			{разблокировываем диспетчер}
			unlock;
		end;
end;

Procedure TDispatcherProcess.delayTimeProcess;
{просто так поместить текущий процесс в список процессов,
 задержанных процессов нельзя}
{нужно ещё передать управление от текущего процесса следующему}
var
	it:	ListIterator;
	cur,
	next:TProcess;
begin
	{если T = 0, то задерживать процесс не надо, выходим}
	if (T = 0)then exit;
	{случай, когда в списке только один процесс не рассматиривается}
	{так как в списке всегда есть управляющий процесс, и его
	задерживать на время нельзя}
	{если удалось записать в cur первый элемент списка готовых
	процессов}

	if (listReadyProcess.front(cur)) then
		begin
			{блокируем диспетчер}
			lock;
			{переносим перывй элемент в конец списка}
			listReadyProcess.move_front_to_back;
			{если удалось записать в переменную next следующий
			процесс}
			if (listReadyProcess.front(next)) then
				begin
					{удаляем из конца списка тот самый теущий
					процесс}
					listReadyProcess.pop_back;
					{задаём у текущего процесса время активизации}
					cur.set_takt(time+T);
					{записываем его в в список процессов,
					задержаннхы на время}
					listDelayTimeProcess.push_back(cur);
					unlock;
					writeln('transfer: pid:', cur.get_id, ' -> pid:',next.get_id);					
					cur.TransferByProcess(next);
				end;
		end;
end;
Procedure TDispatcherProcess.killProcess;
var
	it:ListIterator;{указатель на процесс}
	temp:TProcess;	{временная переменная}
	cur:TProcess;	{текущий процесс}
begin
	
	{пытаемся найти в списке готовых процессов}
	{процесс p, который требуетс уничтожить}
	it:=listReadyProcess.find(p);
	{если нашли}
	if (it <> nil) then
		begin
			{блокируем диспечтер}
			lock;
			listReadyProcess.front(cur);
			{если мы пытаемся уничтожить текущий исполняемый
			процесс}
			if (cur.equal(p)) then
				begin
					{нужно перед удалением текущего процесса
					передать управление следующему}
					{переносим перывй элемент в конец списка}	
					listReadyProcess.move_front_to_back;
					if (listReadyProcess.front(temp)) then
						begin
							{удаляем из конца списка тот самый
							теущий процесс}
							listReadyProcess.pop_back;
							{помещаем в список процессов,
							подлежащих уничтожению, теущий
							процесс}
							listKillProcess.push_back(cur);
							{разблокировываем диспетчер}
							unlock;
							{передаём управление следующему
							процессу, то есть выходим из этого
							метода}
							writeln('transfer: pid:', cur.get_id, ' -> pid:',temp.get_id);
							cur.TransferByProcess(temp);
						end;
				end
			else
				begin
					{копируем искомый процесс в временную
					переменную}
					listReadyProcess.get(it, temp);
					{удаляем искомый процесс из списка готовых
					процессов}
					listReadyProcess.remove(it);
					{добавляем искомый процесс в список
					процессов, подлежащих уничтожению}
					listKillProcess.push_back(temp);
					{разблокировываем диспетчер}
				end;
			unlock;
		end;
end;
Procedure TDispatcherProcess.killProcessByComparator;
var
	it:ListIterator;
	temp,
	cur:TProcess;
begin
	{пытаемся найти в списке готовых процессов}
	{процесс p, который требуетс уничтожить}
	it:=listReadyProcess.find_by_comparator(@p, comp);
	{если нашли}
	if (it <> nil) then
		begin
			{блокируем диспечтер}
			lock;
			listReadyProcess.front(cur);
			{если мы пытаемся уничтожить текущий исполняемый
			процесс}
			if (cur.equal(p)) then
				begin
					{нужно перед удалением текущего процесса
					передать управление следующему}
					{переносим перывй элемент в конец списка}	
					listReadyProcess.move_front_to_back;
					if (listReadyProcess.front(temp)) then
						begin
							{удаляем из конца списка тот самый
							теущий процесс}
							listReadyProcess.pop_back;
							{помещаем в список процессов,
							подлежащих уничтожению, теущий
							процесс}
							listKillProcess.push_back(cur);
							{разблокировываем диспетчер}
							unlock;
							{передаём управление следующему
							процессу, то есть выходим из этого
							метода}
							writeln('transfer: pid:', cur.get_id, ' -> pid:',temp.get_id);
							cur.TransferByProcess(temp);
						end;
				end
			else
				begin
					{копируем искомый процесс в временную переменную}
					listReadyProcess.get(it, temp);
					{удаляем искомый процесс из списка готовых процессов}
					listReadyProcess.remove(it);
					{добавляем искомый процесс в список
					процессов, подлежащих уничтожению}
					listKillProcess.push_back(temp);
					{разблокировываем диспетчер}
				end;
			unlock;
		end;
end;
Procedure TDispatcherProcess.activisation;
var
	it:		ListIterator;
	temp,				{временная переменная}
	searcher:	TProcess;	{переменная для поиска процессоов,
	подлежащих активизации}
begin
	{устанавливаем в процессе 'поисковике' поле takt равным
	текущему времени}
	searcher.set_takt(time);
	{ищем первый процесс с полем takt = текущему времени}
	{с помощью функциии compare_process_by_takt}
	it:=listDelayTimeProcess.find_by_comparator(@searcher, compare_process_by_takt);
	{блокируем диспетчер}
	lock;
	writeln('Поиск задержанного процесса, текущее время : ', time, 'it = ', longint(it));
	{пока есть такие процессы}	
	while (it <> nil) do
		begin
			{записываем очередной активизируемый процесс в
			временную переменную}
			listDelayTimeProcess.get(it, temp);
			{удаляем его из списка процессов, задержанных на
			время}
			listDelayTimeProcess.remove(it);
			{устанавливаем время активации в "никогда"}
			temp.set_takt(NEVER);
			{возвращаем в список готовых процессов}
			listReadyProcess.push_back(temp);
			{ищем снова процесс с полем takt = текущему времени}
			it:=listDelayTimeProcess.find_by_comparator(@searcher, compare_process_by_takt);
		end;
	{разблокировываем диспетчер}
	unlock;	
end;
Procedure TDispatcherProcess.clearKillProcess;
var
	p:TProcess;
begin
	{пока списк не пуст}
	while (not listKillProcess.empty) do
		begin	
			{если удалось записать в p первый процесс}
			if (listKillProcess.front(p)) then
				{уничтожаем его}
				p.done;
			{удаляем из списка процессов, подлежащих
			уничтожению, первый элемент}
			listKillProcess.pop_front;
		end;
end;
Procedure TDispatcherProcess.killCurrentProcess;
var
	cur:TProcess;
begin
	{если получилось в cur записать текущий процесс}
	if (listReadyProcess.front(cur)) then
		begin
			killProcess(cur);
		end;
end;
Procedure TDispatcherProcess.killCurrentProcessByComparator;
var
	cur:TProcess;
begin
	{если получилось в cur записать текущий процесс}
	if (listReadyProcess.front(cur)) then
		begin
			killProcessByComparator(cur, comp);
		end;
end;
Procedure TDispatcherProcess.step_time;
begin
	inc(time);
end;
Procedure TDispatcherProcess.reset_time;
begin
	time:=0;
end;
Function TDispatcherProcess.getNumberReadyProcess;
begin
	getNumberReadyProcess:=listReadyProcess.get_size;
end;
Function TDispatcherProcess.getNumberDelayProcess:word;
begin
	getNumberDelayProcess:=listDelayProcess.get_size;
end;
Procedure TDispatcherProcess.getArrayOfReadyPID;
var
	a:array[1..100]of word absolute arg;
	i:word;
	n:word;		{количество процессов, готовых к выполнению}
	proc:TProcess;
begin
	n:=listReadyProcess.get_size;
	{просматриваем весь список и заполняем массив id процессов
	внутри listReadyProcess}
	for i:=1 to n do
		begin
			listReadyProcess.front(proc);
			a[i]:=proc.get_id;
			listReadyProcess.move_front_to_back;
		end;
end;
Procedure TDispatcherProcess.getArrayOfDelayPID;
var
	a:array[1..100]of word absolute arg;
	i:word;
	n:word;		{количество задержанных процессов}
	proc:TProcess;
begin
	n:=listDelayProcess.get_size;
	{просматриваем весь список и заполняем массив id процессов
	внутри listReadyProcess}
	for i:=1 to n do
		begin
			listDelayProcess.front(proc);
			a[i]:=proc.get_id;
			listDelayProcess.move_front_to_back;
		end;
end;

begin
end.
